イベントシステムをどっかからすげ替える話


概要

完全に私的な実験のメモなので後から読んでも誰にも何もわからんと思う。



現在のタッチと同じRaycasterでどんなターゲットが存在するかを検知する

BaseEventData Constructor


public BaseEventData(EventSystems.EventSystem eventSystem);

Description

Construct a BaseEventData tied to the passed EventSystem.



っつーことなので、これでBaseEventDataが生成できるのでは?


・イベントを生成する

・RaycastAllする


という流れで、位置によってはいけそうな気が。

-> いけた。同じ位置でイベントを発行できる。

で、どんな種類のイベントが発生したかを取得できるだろうか。



GraphicRaycasterを使う手

http://gamedev.stackexchange.com/questions/93592/graphics-raycaster-of-unity-how-does-it-work


よくわかってないが、これがやりたいことは特殊そう。



イベントトリガの話

https://docs.unity3d.com/ScriptReference/EventSystems.EventTrigger.html

EventTriggerを拡張したクラスを作ると、いろんなイベントを傍受することができる?

ぽい?


やってみよう。



uGUIのインスタンスを上から叩く的な。

http://qiita.com/tadokoro/items/e72d6eff5998cb00ab02


やっていることは、

・current.currentSelectedGameObject がヒットするタイミングで、どのgameObjectがどんなポイントでタップされたか、をみる

・そいつと同じクリックを発生させて、可能性を見る

というやつ。


なるほどな~という感じなんだけど、こいつはオブジェクトを見ているあたりに可能性がありそう。



2501の対stateMachineまわりで困っていたのが、

・特定のインスタンスについてるStateMachineが叩けない

というやつで、これをなんとかする必要がある。


具体的には、

・特定のインスタンスを収集する(事前辞書の作成、GOについてるスクリプトのインスタンスの自動収集

・再現系のイベントがインスタンスに対して実行された際、そのインスタンスに対してメソッド実行を送り込む

・するとstateMachineから再現できる

的な。


実行記録からインスタンス割り出しをして実行できるなら、そいつはめっちゃ効率がいい。

メソッド実行のさらに上が必要な場合、それらを取得することも可能だと、可能性が広がる。


という感じ。


uGUI以前のCubeとかに対しての対策

PhysicsRaycasterとかが使えそうな気がする。

-> カメラに対してつけると、cubeとかも全て EventSystem.current.IsPointerOverGameObject() に反応するようになる。


で、イベント獲得 -> currentがnullなら、それは3Dオブジェクト向けのイベントなので、physicsRaycasterでオブジェクト判定して、その

いちばん上を見つける、ということが可能になった。


スゲー野蛮。


これで、記録レイヤーでいうと、

・どの3DオブジェクトがXXXされたか、gameObject単位で取得できるようになった


という感じ。

XXXには、いろんなイベントが入る。

で、記録に関しては光明が見えてきた。



再現

どのイベントが出たかがわかったので、今度は再現時にそれをGOに対してぶつける必要が出てくる。

そういうことができるようなEmit2501形式を実現できればよさげ。


・同じフレームで実行されたコマンドは、なんか把握できるはず。

・(伝搬元のオブジェクトに対してイベントを発行する、とかができるとスゲーー面白い。)


、、、あーーーそういえば実行できましたね、、、うん、、、

ゲームオブジェクト + コンポーネントなので、 stateMachineが付いている場合、そのインスタンスがゲームオブジェクト + コンポーネント名で

保持できていれば、単純にインスタンス名 + OnClickで、発動時にOnClickが起こったことにできるのでは?

-> 理屈上はできる。すげーな。



ラスト、再現用のオブジェクトリストの取得

最後の前提、

ゲーム中に存在しているGUI対象のオブジェクトのリストを取得する。

・GameObjectでMonoBehaviourを持ってるもの

条件を集めて、全体から検索できるようにしよう。クッソ重くてもできる自信はある。根拠は忘れた。



PlayMakerが対応してるイベント一覧

https://hutonggames.fogbugz.com/?W128




https://docs.unity3d.com/jp/540/ScriptReference/EventSystems.BaseEventData.html

ふむ、usedがある。



取得できた!!

<b>eligibleForClick</b>: False クリック時 ~ 放すまでの間にtrueになる。mouseDown, mouseUpの瞬間



階層構造的には、

PointerInputModule -> StandaloneInputModule という順に継承していて、

PointerInputModuleにはm_pointerDataが入っている。


で、


この辞書へは、Process関数でデータが詰められるようになってる。

同じ処理を走らせられれば、どんなイベントが発生するかを追うことは可能。


で、とりあえず今調べたいのは、最速でこのデータを調べた場合、PointerEventDataのbefore-Process-afterの両方を手に入れることで、

どんなイベントが発生したのかを追えるのではないか、という感じ。


そのためには、Processの存在位置が問題になる。


BaseInputModuleでのProcessは、EventSystemから呼ばれてる。

で、そのタイミングは、


Updateだ。よし、設定順が高速なら勝てる。


選択肢が2つ、先回りと後追い + 次フレームの調達というのができそうなんだけど、うーん、、


1.先回り + 現地調達

が確実か。


素早くインスタンスを作る

・Updateで挟む、ということをする。



テスト

・起動時初期化でインスタンスを作る

・シーン起動時に作る

とかで、まずはやって見るか。


どのイベントが発生したかを知る

-> ぶっちゃけ手段がない。

過去直近で発生したイベントを全て経由するポイントとかがあれば、それを見るのがいいんだけど。

-> もっと簡単に、endOfFrame使えば挟めるな。


-> できた。

EndOfFrameと挟み込むことで、イベントの着火を検知できるようになった。

・オブジェクトの検知は1f遅れる(というかendより後、Updateより前で着火してる) -> なので、実際のイベントが発生したのは-1frameになる。



スキャン

対象のオブジェクトが見つかるまで、Rayを打つ、ということができる。

GUIの対象物だけは事前に?

というか

Findすればいいんだよな?ということに気づいた。

名前がわかっていればFindできる。


同じオブジェクトがいっぱいある場合はどうなんだろう。名前で判別がつかないのか。

なにか絶対的なもの = idなんだけど、位置とかもあるはず。

ヒエラルキー位置みたいなのとか。


BaseInputModuleがrequire EventSystemを持ってしまっているので、二重化問題が発生する。

ので、やはりBaseInputModuleから先を模倣しないといけない。EventSystemの模倣になる。


あとは、発生して欲しいイベントがなぜ発生しないのかを追えればいい感じ。

functorか~~なんか違った気がするんだけどな、これらしい。

Enterとかの経路を見てみよう。


対uGUI

click

functor:UnityEngine2.EventSystems.ExecuteEvents+EventFunction`1[UnityEngine.EventSystems.IPointerClickHandler]

UnityEngine.Debug:LogError(Object)

UnityEngine2.EventSystems.ExecuteEvents:Execute(GameObject, BaseEventData, EventFunction`1) (at Assets/UnityEngine.EventSystems/ExecuteEvents.cs:367)

UnityEngine2.EventSystems.StandaloneInputModule2:ProcessMousePress(MouseButtonEventData) (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:547)

UnityEngine2.EventSystems.StandaloneInputModule2:ProcessMouseEvent(Int32) (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:466)

UnityEngine2.EventSystems.StandaloneInputModule2:ProcessMouseEvent() (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:450)

UnityEngine2.EventSystems.StandaloneInputModule2:Process() (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:233)

UnityEngine.EventSystems.EventSystem:Update()


このあと対象のメソッドが呼ばれてる

IPointerClickHandler

UnityEngine.Debug:LogError(Object)

UnityEngine2.EventSystems.ExecuteEvents:Execute(IPointerClickHandler, BaseEventData) (at Assets/UnityEngine.EventSystems/ExecuteEvents.cs:275)

UnityEngine2.EventSystems.ExecuteEvents:Execute(GameObject, BaseEventData, EventFunction`1) (at Assets/UnityEngine.EventSystems/ExecuteEvents.cs:392)

UnityEngine2.EventSystems.StandaloneInputModule2:ProcessMousePress(MouseButtonEventData) (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:547)

UnityEngine2.EventSystems.StandaloneInputModule2:ProcessMouseEvent(Int32) (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:466)

UnityEngine2.EventSystems.StandaloneInputModule2:ProcessMouseEvent() (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:450)

UnityEngine2.EventSystems.StandaloneInputModule2:Process() (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:233)

UnityEngine.EventSystems.EventSystem:Update()



exit

functor:UnityEngine2.EventSystems.ExecuteEvents+EventFunction`1[UnityEngine.EventSystems.IPointerExitHandler]

UnityEngine.Debug:LogError(Object)

UnityEngine2.EventSystems.ExecuteEvents:Execute(GameObject, BaseEventData, EventFunction`1) (at Assets/UnityEngine.EventSystems/ExecuteEvents.cs:367)

UnityEngine2.EventSystems.BaseInputModule:HandlePointerExitAndEnter(PointerEventData, GameObject) (at Assets/UnityEngine.EventSystems/BaseInputModule.cs:190)

UnityEngine2.EventSystems.PointerInputModule:ProcessMove(PointerEventData) (at Assets/UnityEngine.EventSystems/PointerInputModule.cs:284)

UnityEngine2.EventSystems.StandaloneInputModule2:ProcessMouseEvent(Int32) (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:467)

UnityEngine2.EventSystems.StandaloneInputModule2:ProcessMouseEvent() (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:450)

UnityEngine2.EventSystems.StandaloneInputModule2:Process() (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:233)

UnityEngine.EventSystems.EventSystem:Update()


このあと対象のメソッドが呼ばれてる

IPointerExitHandler

UnityEngine.Debug:LogError(Object)

UnityEngine2.EventSystems.ExecuteEvents:Execute(IPointerExitHandler, BaseEventData) (at Assets/UnityEngine.EventSystems/ExecuteEvents.cs:257)

UnityEngine2.EventSystems.ExecuteEvents:Execute(GameObject, BaseEventData, EventFunction`1) (at Assets/UnityEngine.EventSystems/ExecuteEvents.cs:392)

UnityEngine2.EventSystems.BaseInputModule:HandlePointerExitAndEnter(PointerEventData, GameObject) (at Assets/UnityEngine.EventSystems/BaseInputModule.cs:190)

UnityEngine2.EventSystems.PointerInputModule:ProcessMove(PointerEventData) (at Assets/UnityEngine.EventSystems/PointerInputModule.cs:284)

UnityEngine2.EventSystems.StandaloneInputModule2:ProcessMouseEvent(Int32) (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:467)

UnityEngine2.EventSystems.StandaloneInputModule2:ProcessMouseEvent() (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:450)

UnityEngine2.EventSystems.StandaloneInputModule2:Process() (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:233)

UnityEngine.EventSystems.EventSystem:Update()




対オブジェクト

enter

functor:UnityEngine2.EventSystems.ExecuteEvents+EventFunction`1[UnityEngine.EventSystems.IPointerEnterHandler]

UnityEngine.Debug:LogError(Object)

UnityEngine2.EventSystems.ExecuteEvents:Execute(GameObject, BaseEventData, EventFunction`1) (at Assets/UnityEngine.EventSystems/ExecuteEvents.cs:367)

UnityEngine2.EventSystems.BaseInputModule:HandlePointerExitAndEnter(PointerEventData, GameObject) (at Assets/UnityEngine.EventSystems/BaseInputModule.cs:222)

UnityEngine2.EventSystems.PointerInputModule:ProcessMove(PointerEventData) (at Assets/UnityEngine.EventSystems/PointerInputModule.cs:284)

UnityEngine2.EventSystems.StandaloneInputModule2:ProcessMouseEvent(Int32) (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:467)

UnityEngine2.EventSystems.StandaloneInputModule2:ProcessMouseEvent() (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:450)

UnityEngine2.EventSystems.StandaloneInputModule2:Process() (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:233)

UnityEngine.EventSystems.EventSystem:Update()


exit

functor:UnityEngine2.EventSystems.ExecuteEvents+EventFunction`1[UnityEngine.EventSystems.IPointerExitHandler]

UnityEngine.Debug:LogError(Object)

UnityEngine2.EventSystems.ExecuteEvents:Execute(GameObject, BaseEventData, EventFunction`1) (at Assets/UnityEngine.EventSystems/ExecuteEvents.cs:367)

UnityEngine2.EventSystems.BaseInputModule:HandlePointerExitAndEnter(PointerEventData, GameObject) (at Assets/UnityEngine.EventSystems/BaseInputModule.cs:190)

UnityEngine2.EventSystems.PointerInputModule:ProcessMove(PointerEventData) (at Assets/UnityEngine.EventSystems/PointerInputModule.cs:284)

UnityEngine2.EventSystems.StandaloneInputModule2:ProcessMouseEvent(Int32) (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:467)

UnityEngine2.EventSystems.StandaloneInputModule2:ProcessMouseEvent() (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:450)

UnityEngine2.EventSystems.StandaloneInputModule2:Process() (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:233)

UnityEngine.EventSystems.EventSystem:Update()



ProcessMove経由で、HandlePointerExitAndEnterが呼ばれている気がする。

その場所でnullになったり、GOになったりしてる?

pointerEvent.pointerCurrentRaycast.gameObject

にnullが入ってる。


なるほど。



PointerEventDataをoverrideしてると、pointerCurrentRaycast にraycastのsetが行われない、みたいだ。どこで実行されてるか調べたいんだけどな~~。参照を追うしかないか。



ただしいfrom-toは、


みほん CopyFromTo to.pointerCurrentRaycast:Name: Button (UnityEngine.GameObject)

module: Name: Canvas (UnityEngine.GameObject)

eventCamera: 

sortOrderPriority: 0

renderOrderPriority: 0

distance: 0

index: 0

depth: 0

worldNormal: (0.0, 0.0, 0.0)

worldPosition: (0.0, 0.0, 0.0)

screenPosition: (321.9, 129.3)

module.sortOrderPriority: 0

module.renderOrderPriority: 0

sortingLayer: 0

sortingOrder: 0


 vs from.pointerCurrentRaycast:Name: Button (UnityEngine.GameObject)

module: Name: Canvas (UnityEngine.GameObject)

eventCamera: 

sortOrderPriority: 0

renderOrderPriority: 0

distance: 0

index: 0

depth: 0

worldNormal: (0.0, 0.0, 0.0)

worldPosition: (0.0, 0.0, 0.0)

screenPosition: (321.9, 129.3)

module.sortOrderPriority: 0

module.renderOrderPriority: 0

sortingLayer: 0

sortingOrder: 0

UnityEngine.Debug:LogError(Object)

UnityEngine2.EventSystems.PointerInputModule:CopyFromTo(PointerEventData, PointerEventData) (at Assets/UnityEngine.EventSystems/PointerInputModule.cs:207)

UnityEngine2.EventSystems.PointerInputModule:GetMousePointerEventData(Int32) (at Assets/UnityEngine.EventSystems/PointerInputModule.cs:268)

UnityEngine2.EventSystems.StandaloneInputModule2:ProcessMouseEvent(Int32) (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:460)

UnityEngine2.EventSystems.StandaloneInputModule2:ProcessMouseEvent() (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:450)

UnityEngine2.EventSystems.StandaloneInputModule2:Process() (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:233)

UnityEngine.EventSystems.EventSystem:Update()

これに対して、現在は

CopyFromTo to.pointerCurrentRaycast: vs from.pointerCurrentRaycast:

UnityEngine.Debug:LogError(Object)

UnityEngine2.EventSystems.PointerInputModule:CopyFromTo(PointerEventData, PointerEventData) (at Assets/UnityEngine.EventSystems/PointerInputModule.cs:209)

UnityEngine2.EventSystems.PointerInputModule:GetMousePointerEventData(Int32) (at Assets/UnityEngine.EventSystems/PointerInputModule.cs:271)

UnityEngine2.EventSystems.StandaloneInputModule2:ProcessMouseEvent(Int32) (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:466)

UnityEngine2.EventSystems.StandaloneInputModule2:ProcessMouseEvent() (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:455)

UnityEngine2.EventSystems.StandaloneInputModule2:Process() (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:234)

UnityEngine2.EventSystems.EventSystem:Update() (at Assets/UnityEngine.EventSystems/EventSystem.cs:351)



両方ともnull。ふーむ。


-> 自分で作った方は該当するRayCasterがなかった。ので、作ればいけそう。

BaseRaycaster -> GraphinsRaycasterと、PhysicsRaycasterの2つ、、




選択肢は2つあって、

1.Raycasterを自作して追加する

2.RaycastManagerをハックしてリストを取得する


たぶん1のほうがいい。


uGUIに関してはできた。

で、あとは


・GameObjectへのタッチがまだ取れてない -> できた

・uGUIへのイベントの深度がちょっと違う


IPointerEnterHandler

UnityEngine.Debug:LogError(Object)

UnityEngine2.EventSystems.ExecuteEvents:Execute(IPointerEnterHandler, BaseEventData) (at Assets/UnityEngine.EventSystems/ExecuteEvents.cs:251)

UnityEngine2.EventSystems.ExecuteEvents:Execute(GameObject, BaseEventData, EventFunction`1) (at Assets/UnityEngine.EventSystems/ExecuteEvents.cs:392)

UnityEngine2.EventSystems.BaseInputModule:HandlePointerExitAndEnter(PointerEventData, GameObject) (at Assets/UnityEngine.EventSystems/BaseInputModule.cs:231)

UnityEngine2.EventSystems.PointerInputModule:ProcessMove(PointerEventData) (at Assets/UnityEngine.EventSystems/PointerInputModule.cs:301)

UnityEngine2.EventSystems.StandaloneInputModule2:ProcessMouseEvent(Int32) (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:468)

UnityEngine2.EventSystems.StandaloneInputModule2:ProcessMouseEvent() (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:450)

UnityEngine2.EventSystems.StandaloneInputModule2:Process() (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:233)

UnityEngine.EventSystems.EventSystem:Update()

ここまで来る必要がある。



できた~~~



で、Dragとかも補足はできたので、あとはClickのイベント発生の中身を追うとかすると良さげ。

-> 特に同じオブジェクト判定とかしてないっぽいな、、なんか制約があると思ったんだけど。


uGUIの検知が1f遅れる?

なんか初期化順の問題なのかな


my processのほうが実行が早いのに、判定は遅れる。

これはバグっぽい。よくない。


IPointerClickHandler:88

UnityEngine.Debug:LogWarning(Object)

UnityEngine2.EventSystems.ExecuteEvents:Execute(IPointerClickHandler, BaseEventData) (at Assets/UnityEngine.EventSystems/ExecuteEvents.cs:276)

UnityEngine2.EventSystems.ExecuteEvents:Execute(GameObject, BaseEventData, EventFunction`1) (at Assets/UnityEngine.EventSystems/ExecuteEvents.cs:401)


UnityEngine2.EventSystems.StandaloneInputModule2:ProcessMousePress(MouseButtonEventData) (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:556)

UnityEngine2.EventSystems.StandaloneInputModule2:ProcessMouseEvent(Int32) (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:475)

UnityEngine2.EventSystems.StandaloneInputModule2:ProcessMouseEvent() (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:456)

UnityEngine2.EventSystems.StandaloneInputModule2:Process() (at Assets/UnityEngine.EventSystems/StandaloneInputModule.cs:235)

UnityEngine2.EventSystems.EventSystem:Update() (at Assets/UnityEngine.EventSystems/EventSystem.cs:350)


この判定がもう一歩高速でないとおかしい。Updateか。



Processで、ProcessMousePressまではきてる。

で、ここからの挙動に差がある。

ReleasedThisFrame

なるほど、Update時にはtrueになってないんだ。ということは、

・Update

・何か

・EventSystemが処理する

という順で動いている?それも妙な話だな。



processing. count:1208 button:True

UnityEngine.Debug:LogError(Object)

MyInputModule:Process() (at Assets/MyInputModule.cs:14)

UnityEngine.EventSystems.EventSystem:Update()


この中に、Input.GetMouseButtonUpをtrueにしている要素があるはず。

ってなるとInputなんだけどねえ。



思い違いをしていた。

eventSystemは、endOfFrameよりあと、次のUpdateより前で動いている。

つまりeventSystemの使っているUpdateがすごく特殊。


-> 組み込みのExecutionOrderがあるみたい。なので、次の結論が得られた。


・EventSystemが既に存在する場合のみ、順に配慮した動きが必要になる

・5.5以上でないと多分追い抜けない(それでも追い抜けないかもしれない

-> tapでインスタンスがnullにされたりすると、事後でしか追いつけないので、記録が同じにならない可能性がある

-> 直前のフレームに処理を差し込むにはEventSystemに乗っかるか、先に実行するしかない。乗っかる方は辛いのでやりたくない。


・eventSystemが存在しなければ特に問題なさげ?

-> 腕力でexecutionOrderいじりたい

-> metadata編集で勝てた。-1000つよい。

-10 x

-100 x

-999 x

-1000 o


イェーイ。

ということで、運用するなら、metadataを開けてなんとかする機構が必要になる。

そしてこの機構のために、この部分だけをコードで用意する必要がある。


なるほど。